package com.ultron.gateway.filter;


import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ultron.common.entity.auth.AuthBaseVO;
import com.ultron.common.entity.system.Result;
import com.ultron.common.entity.system.ResultEnum;
import com.ultron.common.util.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * 标题：AuthGlobalFilter
 * 说明：GatetWay 全局授权过滤器
 * 时间：2023/8/30
 * 作者：admin
 */

@Order(-1)
@Configuration
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Value("${access-token-name}")
    private String accessTokenName;

    /**
     * JWT KEY
     */
    @Value("${jwt-key}")
    private String jwtKey;

    /**
     * 白名单列表
     */
    @Value("${white-uris}")
    private  String[] WHITE_URIS;

    /**
     * 参数携带Token名
     */
    private final static String paramTokenName = "token";







    /**
     * GatetWay 全局过滤器
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        boolean isWhitelist = this.isWhitelist(request);
        //1、白名单放行
        if (isWhitelist) {
            return chain.filter(exchange);
        }
        //2、检查token是否存在
        String token = this.getToken(request);
        if(StringUtils.isBlank(token)){
            return noTokenMono(exchange);
        }
        //3、判断是否是有效的token并传递token到微服务
        boolean isPermission = this.isPermission(token);
        if(isPermission){
            return chain.filter(exchange);
        }else {
            return invalidTokenMono(exchange);
        }
    }


    /**
     * 是否在白名单
     * @param request
     * @return
     */
    private boolean isWhitelist(ServerHttpRequest request){
        boolean rsp = false;
        String reqUrl = request.getPath().value();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for(String whiteUri:this.WHITE_URIS){
            //1、白名单放行:pathMatcher.match("/api/uaa/**", reqUrl)
            if (pathMatcher.match(whiteUri, reqUrl)) {
                rsp = true;
                break;
            }
        }

        return rsp;
    }


    /**
     * 获取token
     * @param request
     * @return
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(this.accessTokenName);
        //第一次为空，则看参数中是否带
        if (StringUtils.isBlank(token)) {
            token = request.getQueryParams().getFirst(paramTokenName);
        }
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return token;
    }



    /**
     * token是否有效
     * @param token
     * @return
     */
    private boolean isPermission(String token){
        boolean rsp = false;
        JWT jwt = JWTUtil.parseToken(token);
        JWTPayload payload = jwt.getPayload();
        AuthBaseVO authBaseVO = JSONUtil.toBean(payload.getClaimsJson(), AuthBaseVO.class);
        Date expireTime = authBaseVO.getExpireTime();
        boolean verify = JWTUtil.verify(token, jwtKey.getBytes());
        if(expireTime.compareTo(DateUtils.getCurrentDateYYYYMMDDHHMMSS()) > 0 && verify){
            rsp = true;
        }
        return rsp;
    }




    /**
     * 未携带token
     * @param exchange
     * @return
     */
    private Mono<Void> noTokenMono(ServerWebExchange exchange) {
        JSONObject json = JSONObject.parseObject(JSON.toJSONString(Result.fail(ResultEnum.UN_AUTHORIZED,"未授权访问！"), SerializerFeature.WriteNullStringAsEmpty));
        return buildReturnMono(json, exchange);
    }

    /**
     * token 过期
     * @param exchange
     * @return
     */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
        JSONObject json = JSONObject.parseObject(JSON.toJSONString(Result.fail(ResultEnum.UN_AUTHORIZED,"无效的授权！"), SerializerFeature.WriteNullStringAsEmpty));
        return buildReturnMono(json, exchange);
    }

    private Mono<Void> buildReturnMono(JSONObject json, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = json.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
        return response.writeWith(Mono.just(buffer));
    }





    @Override
    public int getOrder() {
        return 0;
    }
}
